#import "TuioClient.h"
#import "TuioCocoaBind.h" 
#import <JavaVM/JavaVM.h>

@implementation TuioClient : QCPatch

// share one client for all TuioClient patches (saved resources)
//
static TuioCocoaBind * moTuioCocoaBind;

// only print debug info for Debug Build Configuration
//
#ifdef TUIO_DEBUG
   BOOL mbDebug = YES;
#else
   BOOL mbDebug = NO;
#endif

+ (int)executionMode
{
	// Controller - blue title bar
   // 
	return 2;
}

+ (BOOL)allowsSubpatches
{
	return NO;
}

// private
//
+ (void)prepareJavaVM
{
	//Only one vm is needed for all accounts
	static NSJavaVirtualMachine *vm = nil;

	@synchronized(vm) 
   {
		if (!vm) 
      {
			NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

			NSString	*tuioJarPath;
			NSString	*classPath;

			tuioJarPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"TuioClientClasses"
                                                               ofType:@"jar"
																	            inDirectory:@"Java"];
                                                               			
			classPath = [NSString stringWithFormat:@"%@:%@",
				[NSJavaVirtualMachine defaultClassPath],tuioJarPath];
			
			vm = [[NSJavaVirtualMachine alloc] initWithClassPath:classPath]; 
               
			if (!NSClassFromString(@"TuioCocoaBind")) {
				NSMutableString	*msg = [NSMutableString string];
				
				[msg appendFormat:@"Java version %@ could not load TuioCocoaBind\n",[NSClassFromString(@"java.lang.System") getProperty:@"java.version"]];
				[msg appendFormat:@"TuioClientClasses.jar %@\n", ((NSClassFromString(@"TuioCocoaBind") != NULL) ? @"loaded" : @"NOT loaded")];
				[msg appendFormat:@"\nClass path: %@\n\n",classPath];

				NSLog( @"Error loading java: %@", msg );
			}
			
			[pool release];
		}
	}
}

- (id)setup:(id)p
{
	if( [inputUDPPort indexValue] == 0 )
   {
      [inputUDPPort setIndexValue:3333];
   }
   [outputOnScreen setBooleanValue:NO];

   ////////////////////////////////////////////////////////////////////////////////////////////
   // TUIO client integration
   // [Create and setup the Tuio listening client]
   
   @try
   {
      if( moTuioCocoaBind == nil )
      {
         //Class loClass = NSClassFromString(@"TuioCocoaBind");
         //NSLog( @"class %@", [loClass className] );
         //moTuioCocoaBind = [[loClass alloc] init];

         [TuioClient prepareJavaVM];

		   NSLog( @"Java version: %@", [NSClassFromString(@"java.lang.System") getProperty:@"java.version"] );
         
         moTuioCocoaBind = [[NSClassFromString(@"TuioCocoaBind") alloc] init];

         NSLog( @"class %@", [moTuioCocoaBind className] );
         
         [moTuioCocoaBind initialize:[inputUDPPort indexValue]:mbDebug];

         if( mbDebug )
            NSLog( @"Tuio: Client setup" );
      }
      else
      {
         if( mbDebug )
            NSLog( @"Tuio: Client already setup" );
      }
   } 
   @catch ( NSException *e )
   {
      if( mbDebug )
         NSLog( @"Tuio: Client setup error: @%", [e reason] );
   
      [moTuioCocoaBind release];
   }

   // end TUIO client integration
   ////////////////////////////////////////////////////////////////////////////////////////////

	return p;
}

- (BOOL)execute:(QCOpenGLContext*)qcglctx time:(double)exec_time arguments:(id)arg
{   
   @synchronized( moTuioCocoaBind )
   {
      // Should not occur, but in case it does return to prvent all sorts of QC crashes
      //
      if( moTuioCocoaBind == nil )
      {
         return YES;
      }
   }
   
   double liX = 0;
   double liY = 0;
   double liA = 0;

	/*
   // INVESTIGATE: Maybe we need the OpenGL Context to determine the right scaling numbers 
   // for the X and Y output
   //
   CGLContextObj qc_ctx = [qcglctx CGLContextObj];

   GLsizei liCanvasWidth = 640; 
   GLsizei liCanvasHeight = 480;
   
   GLint rowBytes; 
   void *offscreenBuffer = NULL; 
   
   CGLError liError = CGLGetOffScreen
   (
      qc_ctx, 
      &liCanvasWidth, 
      &liCanvasHeight,
      &rowBytes,
      &offscreenBuffer
   );
   
   if( liError != kCGLNoError )
   {
      NSLog( @"@%", CGLErrorString( liError ) );
   }
   */

   ////////////////////////////////////////////////////////////////////////////////////////////
   // TUIO client integration
   // [Retrieve the Fiducial info]
   
   @try 
   {
      TuioCocoaFiducialData *loTuioData = [[NSClassFromString(@"TuioCocoaFiducialData") alloc] init];
      
      BOOL lbHasData;
      
      @synchronized( moTuioCocoaBind )
      {
         lbHasData = [moTuioCocoaBind getObjectInfo:loTuioData:[inputUDPPort indexValue]:[inputFiducial indexValue]];
      }

      if( loTuioData  != nil && lbHasData == YES )
      {
         [outputOnScreen setBooleanValue:YES];

         if( [loTuioData count] >= 3 )
         {
            liX = [loTuioData getDouble:0]; 
            liY = (double)1 - [loTuioData getDouble:1];
            liA = [loTuioData getDouble:2]; 
            
            liX = (liX - (double)0.5); // needs adjustment ( -0.93 <> 0.93 )
            liY = (liY - (double)0.5); // needs adjustment ( 0.73 <> -0.73 )

            // transform angle to degrees
            // 
            if( liA >= (double)0 && liA <= (double)pi ) 
               liA = liA + (double)pi;
            else 
               liA = liA - (double)pi;
            liA = round( liA * (double)360 / (pi*(double)2) );
            
            if( mbDebug )
               NSLog( @"Tuio: Fiducial (%i) data: %f,%f,%f",[inputFiducial indexValue],liX,liY,liA);
         }
         else
         {
            if( mbDebug )
               NSLog( @"Tuio: Result less then 3 items");
         }
      }
      else
      {
         [outputOnScreen setBooleanValue:NO];
      }
      
      [loTuioData release];
   } 
   @catch ( NSException *e )
   {
      if( mbDebug )
         NSLog( @"Tuio: execute error: %@", [e reason] );
   }

   // end TUIO client integration
   ////////////////////////////////////////////////////////////////////////////////////////////

   
   [outputX setDoubleValue:liX];
   [outputY setDoubleValue:liY];
   [outputA setDoubleValue:liA];
   

   
   return YES;
}

- (void)cleanup:(id)p
{
   ////////////////////////////////////////////////////////////////////////////////////////////
   // TUIO client integration
   // [Close the Tuio client]
   
   if( moTuioCocoaBind == nil )
   {
      if( mbDebug )
         NSLog( @"Tuio: Client already removed" );
         
      return;
   }
   
   @synchronized( moTuioCocoaBind )
   {         
      if( mbDebug )
         NSLog( @"Tuio: Client cleaning up" );
         
      @try
      {
         [moTuioCocoaBind disconnect];
         [moTuioCocoaBind release];
         moTuioCocoaBind = nil;
         
         if( mbDebug )
            NSLog( @"Tuio: Client removed" );
      }
      @catch ( NSException *e )
      {
         if( mbDebug )
            NSLog( @"Tuio: cleanup error: %@", [e reason] );
      }
   }
   // end TUIO client integration
   ////////////////////////////////////////////////////////////////////////////////////////////
}

@end